import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { ReactNode } from 'react';
import { ITree, treeMap } from '../../utils/utility';
import { Icon } from '../icon';
import { Text } from '../text';

export interface ITreeItemProps {
  expanded: boolean;
  checked: boolean;
  title: string;
  onExpandedToggle?: React.MouseEventHandler<Element>;
  onCheckedToggle?: React.MouseEventHandler<Element>;
  checkable?: boolean;
  hasChildren?: boolean;
  className?: string;
}

const TREE_NODE_HEIGHT = 28;

const TreeItem = (props: ITreeItemProps) => (
  <div className={props.className}>
    {props.hasChildren ? <Icon
      onClick={props.onExpandedToggle}
      type={props.expanded ? 'subtract-case' : 'add-case'} style={{ marginRight: '4px' }} color="#b8b8b8" /> : null}
    {props.checkable ? <Icon
      onClick={props.onCheckedToggle}
      type={props.checked ? 'selected' : 'unselected'}
      style={{ marginRight: '4px' }}
      color={props.checked ? '#e22e33' : '#b8b8b8'} /> : null}
    <Text className="br-tree-node__title" ellipsis title={props.title}>{props.title}</Text>
  </div>
);

export interface ITreeNodeProps {
  defaultTree?: ITree;
  treeNode: ITree;
  style?: React.CSSProperties;
  className?: string;
  onChange?: (tree: ITree) => void;
}

export interface ISingleTreeState {
  tree: ITree;
}

export class TreeNode extends React.Component<ITreeNodeProps, ISingleTreeState> {
  public static TREE_NODE_HEIGHT = TREE_NODE_HEIGHT;

  public static propTypes = {
    style: PropTypes.object,
    className: PropTypes.string,
    treeNode: PropTypes.shape({
      key: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      children: PropTypes.array,
    }),
    onChange: PropTypes.func,
  };

  // 如果传入了外部props，更新到state
  public static getDerivedStateFromProps(nextProps: ITreeNodeProps, prevState: ISingleTreeState) {
    return nextProps.treeNode ? { tree: nextProps.treeNode } : null;
  }

  constructor(props: ITreeNodeProps) {
    super(props);
    this.renderNode = this.renderNode.bind(this);
    this.state = { tree: this.props.defaultTree || { key: 'default', title: '没有数据' } };
  }

  public isLeaf(node: ITree) {
    return !(node.children && node.children.length);
  }

  public renderNode(treeNode: ITree, onChange?: (treeNode: ITree) => void) {
    const hasChildren = treeNode.children && treeNode.children.length;

    let children: ReactNode[] = [];

    // 处理选中
    const handleCheckedToggle = () => {
      if (onChange) {
        const checked = !treeNode.checked;
        onChange({
          ...treeNode,
          checked,
          children: treeNode.children && treeNode.children.map((node) => {
            return treeMap(node, (data) => ({ ...data, checked }));
          }),
        });
      }
    };

    // 处理展开
    const handleExpandedToggle = () => {
      if (onChange) {
        onChange({ ...treeNode, expanded: !treeNode.expanded });
      }
    };

    if (treeNode.expanded && treeNode.children) {
      children = treeNode.children.map((node, index) => {
        // 展开时，引用parent，减少遍历操作
        return this.renderNode(node, (newNode: ITree) => {
          if (onChange) {
            const newChildren = [
              ...(treeNode.children || []).slice(0, index),
              newNode,
              ...(treeNode.children || []).slice(index + 1),
            ];
            onChange({
              ...treeNode,
              checked: !newChildren.find((childNode) => !childNode.checked),
              children: newChildren,
            });
          }
        });
      });
    }

    const props: ITreeItemProps = {
      title: treeNode.title,
      expanded: !!(hasChildren && treeNode.expanded),
      checked: !!treeNode.checked,
      hasChildren: !!hasChildren,
      checkable: true, // 暂时全部为true
      onCheckedToggle: handleCheckedToggle,
      onExpandedToggle: handleExpandedToggle,
    };

    return (
      <li
        key={treeNode.key}
        className={classNames('br-tree-node', this.props.className)}
      >
        <TreeItem {...props}
          className={
            classNames('br-tree-node__item',
              { 'br-tree-node__item--leaf': this.isLeaf(treeNode) })
          }
        />
        <ul className="br-tree-node__children">{children}</ul>
      </li>
    );
  }

  public render() {
    return this.renderNode(this.state.tree,
      (node) => this.props.onChange && this.props.onChange(node));
  }
}
